package service

import (
	"duoke/context"
	"duoke/internal/dao"
	"duoke/internal/model/entity"
	"duoke/model"
	"duoke/tables"
	utils "duoke/utils/common"
	"duoke/utils/gconv"
	"duoke/utils/orm"
	"duoke/utils/storage"
	"duoke/utils/wechat_v3"
	"duoke/utils/yaml"
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/go-pay/gopay"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/mitchellh/mapstructure"
	"github.com/sirupsen/logrus"
	"strings"
)

var SeparateAccounts = new(separateAccountsService)

type separateAccountsService struct{}

func (s *separateAccountsService) Page(c *gin.Context, req model.ReqList) ([]model.SeparateAccountsList,
	int64, error) {
	accountR, _ := Request.Account(c)
	// 分页
	list := make([]model.SeparateAccountsList, 0)
	offset := (req.PageNo - 1) * req.PageSize
	query := orm.Db.Desc("id")
	query.Where("acid =?", accountR.Id)
	if req.Keyword != "" {
		query = query.
			Where("name like ? or sub_mch_name like ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
	}
	//判断子账户
	total, err := query.Limit(req.PageSize, offset).FindAndCount(&list)
	if err != nil {
		return nil, 0, err
	}
	a := new(areaService)
	for i, v := range list {
		id, _ := a.InfoById(v.Area, "mergename")
		if id != nil {
			array := strings.Split(id.Mergename, ",")
			list[i].ProvinceName = array[1]
			if len(array) > 2 {
				list[i].CityName = array[2]
			}
			if len(array) > 3 {
				list[i].AreaName = array[3]
			}
		}
	}
	return list, total, nil
}

func (s *separateAccountsService) Save(c *gin.Context, req *model.ReqSeparateAccounts) error {
	accountR, _ := Request.Account(c)
	if req.SubMchName == "" {
		return errors.New("参数错误，【sub_mch_name】必填")
	}
	go func() {
		res, err := AccountSetting.InfoById(accountR.Id, "miniprogram")
		if err != nil {
			logrus.Error("小程序配置不存在：%s", err.Error())
		}
		// 分账流程
		account := wechat_v3.SeparateConfig
		account.AppId = gconv.String(res["app_id"])
		account.PayCertPath = gconv.String(res["pay_cert_path"])
		account.PayKeyFilePath = gconv.String(res["pay_cert_key_path"])
		account.PaySerialNo = gconv.String(res["pay_serial_no"])
		account.PayKeyV3 = gconv.String(res["pay_key_v3"])
		account.PayMchId = gconv.String(res["pay_mch_id"])
		account.PayKey = gconv.String(res["pay_key"])
		_, err = account.SubAccount(c, &model.ReqSubAccount{
			SubMchId:              req.SubMchId,
			ReceivierType:         "MERCHANT_ID",
			ReceivierAccount:      gconv.String(res["pay_mch_id"]),
			ReceivierName:         gconv.String(res["mch_name"]),
			ReceivierRelationType: "SERVICE_PROVIDER",
		})
		if err != nil {
			logrus.Error("用户绑定失败：%s:", err.Error())
		}
	}()
	var url string
	code, err := s.GenerateCode(c, req.SubMchId, "pages/payInfo/pay/index")
	if code != nil || err == nil {
		url = code.Url
		req.SmallProgramCode = url
	}
	// end
	if req.Id > 0 {
		// 保存数据
		_, err := orm.Db.ID(req.Id).Where("acid =?", accountR.Id).Update(req)
		if err != nil {
			return err
		}
	} else {
		// 保存数据
		req.Acid = accountR.Id
		_, err := orm.Db.Insert(req)
		if err != nil {
			return err
		}
	}
	return nil
}
func (s *separateAccountsService) Del(c *gin.Context, req model.ReqId) error {
	accountR, _ := Request.Account(c)
	_, err := orm.Db.ID(req.Id).Where("acid =?", accountR.Id).Delete(new(tables.SeparateAccounts))
	if err != nil {
		return err
	}
	return nil
}

// 商户信息
func (s *separateAccountsService) Info(c *gin.Context, field string, subMchId string) (
	*tables.SeparateAccounts, error) {
	user := new(tables.SeparateAccounts)
	has, err := orm.Db.Where("sub_mch_id=?", subMchId).Cols(field).Get(user)
	if err != nil {
		return nil, err
	}
	if !has {
		return nil, errors.New("商户信息不存在")
	}
	return user, nil
}
func (s *separateAccountsService) mergeData(data []map[string]interface{}) []map[string]interface{} {
	// 临时 map：键为 account，值为合并后的 map
	merged := make(map[string]map[string]interface{})
	for _, item := range data {
		account, ok := item["account"].(string)

		if !ok {
			continue
		}
		amount, ok := item["amount"].(int)
		if !ok {
			continue
		}
		if amount == 0 {
			continue
		}
		if _, exists := merged[account]; !exists {
			merged[account] = make(map[string]interface{})
			merged[account]["type"] = gconv.String(item["type"])
			merged[account]["account"] = account
			merged[account]["amount"] = 0
		}
		currentAmount := merged[account]["amount"].(int)
		merged[account]["amount"] = currentAmount + amount
		merged[account]["description"] = gconv.String(item["description"])
	}
	result := make([]map[string]interface{}, 0, len(merged))
	for _, value := range merged {
		result = append(result, value)
	}
	return result
}

// 分账逻辑
func (s *separateAccountsService) SeparateAccounts(c *gin.Context, req gopay.BodyMap) error {
	// 先分账给用户
	mchId := req.GetString("mch_id")
	subMchId := req.GetString("sub_mch_id")
	orderId := req.GetString("out_trade_no")
	info, err := SeparateAccounts.Info(c, "user_commission,platform_commission,commission,acid,name",
		subMchId)

	var coupon *entity.CouponOrder
	err = dao.CouponOrder.Ctx(c).Where("order_no", orderId).Scan(&coupon)
	if err != nil {
		return err
	}
	// 先分用户
	var data []map[string]interface{}
	// 平台拿出分账金额
	totalFee := gconv.Float64(100) * (gconv.Float64(info.PlatformCommission) / 100)
	amount := gconv.Int(gconv.Float64(gconv.Float64(info.UserCommission)*totalFee) / 100)
	data = append(data, map[string]interface{}{
		"order_id":      orderId,
		"type":          "PERSONAL_OPENID",
		"account":       req.GetString("openid"),
		"amount":        amount,
		"description":   "购买赠送",
		"platform_name": info.Name,
		"group_name":    "用户返额",
		"sub_mch_id":    subMchId,
		"commission":    info.UserCommission,
	})
	remainingAmount := amount
	// 分发区域代理
	list := new(separateAccountsSubService)
	sub, err := list.ListBySubMchId("account,type", subMchId)
	if err != nil {
		return err
	}
	for _, item := range sub {
		amount = gconv.Int(gconv.Float64(gconv.Float64(item.Group.Commission)*totalFee) / 100)
		data = append(data, map[string]interface{}{
			"order_id":      orderId,
			"type":          item.Type,
			"account":       item.Account,
			"amount":        amount,
			"description":   "分账区域赠送",
			"platform_name": info.Name,
			"group_name":    item.Group.Name,
			"sub_mch_id":    subMchId,
			"commission":    item.Group.Commission,
		})
		remainingAmount += amount
	}
	// 分发平台
	amount = gconv.Int(gconv.Float64(gconv.Float64(info.Commission)*totalFee) / 100)
	data = append(data, map[string]interface{}{
		"order_id":      orderId,
		"type":          "MERCHANT_ID",
		"account":       mchId,
		"amount":        amount,
		"description":   "平台抽成",
		"platform_name": info.Name,
		"group_name":    "平台",
		"sub_mch_id":    subMchId,
		"commission":    info.Commission,
	})
	remainingAmount += amount
	remainingAmount = gconv.Int(totalFee) - remainingAmount
	if remainingAmount > 0 {
		data = append(data, map[string]interface{}{
			"order_id":      orderId,
			"type":          "MERCHANT_ID",
			"account":       mchId,
			"amount":        remainingAmount,
			"description":   "平台抽成",
			"platform_name": info.Name,
			"group_name":    "剩余金额转入",
			"sub_mch_id":    subMchId,
			"commission":    info.Commission,
		})
	}

	// 插入数据表
	var shareOrder []tables.ShareOrder
	err = mapstructure.Decode(data, &shareOrder)
	if err != nil {
		return err
	}
	s2 := new(shareOrderService)
	err = s2.SaveAll(&shareOrder)
	if err != nil {
		return nil
	}
	// 唯一数组
	newData := s.mergeData(data)
	set := new(accountSettingService)
	res, err := set.InfoById(info.Acid, "miniprogram")
	if err != nil {
		return err
	}
	share, err := wechat_v3.NewConfig(res).Share(map[string]string{
		"sub_mch_id":     subMchId,
		"transaction_id": req.GetString("transaction_id"),
		"out_order_no":   req.GetString("out_trade_no"),
	}, newData)
	if err != nil {
		return err
	}
	logrus.Info("分账信息：", share)
	if share.ResultCode == "FAIL" {
		logrus.Error("分账失败原因：", share.ErrCodeDes)
	}
	if share.ResultCode == "FAIL" {
		// 这里修改状态为2
		_, err = dao.CouponOrder.Ctx(c).Data(g.Map{"separate_state": 2}).
			Where("order_no", orderId).Unscoped().Update()
		if err != nil {
			return err
		}
	} else {
		//这里修改状态为1
		_, err = dao.CouponOrder.Ctx(c).Data(g.Map{"separate_state": 1}).
			Where("order_no", orderId).Unscoped().Update()
		if err != nil {
			return err
		}
	}
	return nil
}

// 分账逻辑
func (s *separateAccountsService) SeparateAccountsV2(c *gin.Context, req gopay.BodyMap) error {
	// 先分账给用户
	mchId := req.GetString("mch_id")
	subMchId := req.GetString("sub_mch_id")
	orderId := req.GetString("out_trade_no")
	totalFee := gconv.Float64(req.GetString("total_fee"))
	info, err := SeparateAccounts.Info(c, "user_commission,platform_commission,commission,acid,name",
		subMchId)
	// 先分用户
	var data []map[string]interface{}
	// 平台拿出分账金额
	totalFee = totalFee * (gconv.Float64(info.PlatformCommission) / 100)
	// 平台分成
	amount := gconv.Int(gconv.Float64(gconv.Float64(info.UserCommission)*totalFee) / 100)
	data = append(data, map[string]interface{}{
		"order_id":      orderId,
		"type":          "PERSONAL_OPENID",
		"account":       req.GetString("openid"),
		"amount":        amount,
		"description":   "购买赠送",
		"platform_name": info.Name,
		"group_name":    "用户返额",
		"sub_mch_id":    subMchId,
		"commission":    info.UserCommission,
	})
	remainingAmount := amount
	// 分发区域代理
	list := new(separateAccountsSubService)
	sub, err := list.ListBySubMchId("account,type", subMchId)
	if err != nil {
		return err
	}
	for _, item := range sub {
		amount = gconv.Int(gconv.Float64(gconv.Float64(item.Group.Commission)*totalFee) / 100)
		data = append(data, map[string]interface{}{
			"order_id":      orderId,
			"type":          item.Type,
			"account":       item.Account,
			"amount":        amount,
			"description":   "商户抽成",
			"platform_name": info.Name,
			"group_name":    item.Group.Name,
			"sub_mch_id":    subMchId,
			"commission":    item.Group.Commission,
		})
		remainingAmount += amount
	}
	// 分发平台
	amount = gconv.Int(gconv.Float64(gconv.Float64(info.Commission)*totalFee) / 100)
	data = append(data, map[string]interface{}{
		"order_id":      orderId,
		"type":          "MERCHANT_ID",
		"account":       mchId,
		"amount":        amount,
		"description":   "平台抽成",
		"platform_name": info.Name,
		"group_name":    "平台",
		"sub_mch_id":    subMchId,
		"commission":    info.Commission,
	})
	remainingAmount += amount
	remainingAmount = gconv.Int(totalFee) - remainingAmount
	if remainingAmount > 0 {
		data = append(data, map[string]interface{}{
			"order_id":      orderId,
			"type":          "MERCHANT_ID",
			"account":       mchId,
			"amount":        remainingAmount,
			"description":   "平台抽成",
			"platform_name": info.Name,
			"group_name":    "剩余金额转入",
			"sub_mch_id":    subMchId,
			"commission":    info.Commission,
		})
	}

	// 插入数据表
	var shareOrder []tables.ShareOrder
	err = mapstructure.Decode(data, &shareOrder)
	if err != nil {
		return err
	}
	s2 := new(shareOrderService)
	err = s2.SaveAll(&shareOrder)
	if err != nil {
		return nil
	}
	// 唯一数组
	newData := s.mergeData(data)
	set := new(accountSettingService)
	res, err := set.InfoById(info.Acid, "miniprogram")
	if err != nil {
		return err
	}
	share, err := wechat_v3.NewConfig(res).Share(map[string]string{
		"sub_mch_id":     subMchId,
		"transaction_id": req.GetString("transaction_id"),
		"out_order_no":   req.GetString("out_trade_no"),
	}, newData)
	if err != nil {
		return err
	}
	logrus.Info("分账信息：", share)
	if share.ResultCode == "FAIL" {
		return errors.New(share.ErrCodeDes)
	}
	return nil
}
func (s *separateAccountsService) GenerateCode(c *gin.Context, sub_mch_id string, path string) (
	*storage.StructFile, error) {
	user, err := Request.Account(c)
	if err != nil {
		return nil, err
	}
	set := new(accountSettingService)
	res, err := set.InfoById(user.Id, "miniprogram")
	if err != nil {
		return nil, err
	}
	unlimited, err := wechat_v3.SmallProgram.NewConfig(res).GetUnlimited(path, "sub_mch_id="+sub_mch_id)
	if err != nil {
		return nil, err
	}
	// 基础
	setting, err := Upload.Setting(c)
	uploader, err := storage.Qiniu.NewFormUploader(unlimited,
		&storage.FormUploader{
			Suffix:  ".png",
			Acid:    user.Id,
			Setting: setting,
		})
	if err != nil {
		return nil, err
	}
	uploader.GroupId = gconv.Int(0)
	err = Upload.Save(c, *uploader)
	return uploader, nil
}
func (s *separateAccountsService) GenerateCodeV2(c *gin.Context, req *model.GenerateCode) (*storage.StructFile,
	error) {
	user, err := Request.Account(c)
	if err != nil {
		return nil, err
	}
	set := new(accountSettingService)
	res, err := set.InfoById(user.Id, "miniprogram")
	if err != nil {
		return nil, err
	}
	unlimited, err := wechat_v3.SmallProgram.NewConfig(res).GetUnlimited(req.Path, req.Param)
	if err != nil {
		return nil, err
	}
	// 基础
	setting, err := Upload.Setting(c)
	uploader, err := storage.Qiniu.NewFormUploader(unlimited,
		&storage.FormUploader{
			Suffix:  ".png",
			Acid:    user.Id,
			Setting: setting,
		})
	if err != nil {
		return nil, err
	}
	uploader.GroupId = gconv.Int(0)
	err = Upload.Save(c, *uploader)
	return uploader, nil
}

// 保存成功
func (s *separateAccountsService) SaveV2(c *gin.Context, req *model.ReqSeparateAccounts) error {
	accountR, err := Request.Account(c)
	if err != nil {
		return err
	}
	// end
	if req.Id > 0 {
		// 保存数据
		_, err = orm.Db.ID(req.Id).Where("acid =?", accountR.Id).Update(req)
		if err != nil {
			return err
		}
	} else {
		// 保存数据
		req.Acid = accountR.Id
		_, err = orm.Db.Insert(req)
		if err != nil {
			return err
		}
	}
	return nil
}

// CreateOrder 创建订单
func (s *separateAccountsService) CreateOrder(c *gin.Context, req *model.CreateOrder) (string, error) {
	user, err := User.Info(c, "openid,ip_addr,id,acid")
	if err != nil {
		return "", err
	}
	_, err = s.Info(c, "*", req.SubMchId)
	if err != nil {
		return "", err
	}
	orderNo := utils.Common.GetOrder()
	// 未购买过 构造参数
	shorOrder := tables.SepatateOrder{
		UserId:      user.Id,
		TotalAmount: req.Amount,
		OrderNo:     orderNo,
		SubMchId:    req.SubMchId,
		IpAddr:      user.IpAddr,
		Body:        req.Body,
		Acid:        user.Acid,
		Openid:      user.Openid,
		State:       2,
	}
	_, err = dao.SepatateOrder.Ctx(c).Save(&shorOrder)
	if err != nil {
		return "", err
	}
	return orderNo, nil
}

// PayOrder 订单支付
func (s *separateAccountsService) PayOrder(c *gin.Context, m *tables.CourseOrder) (interface{}, error) {
	// 构建支付参数
	notifyUrl := context.Global.Domain + "/mp/notify/separate_notify"
	config := yaml.GetConfig()
	if config.SubMch.SubType == "2" {
		m.SubMchId = ""
	}
	payConf, err := WechatPay.OrderV2(c, notifyUrl, m.Openid, m.OrderNo,
		m.TotalAmount, m.Body, m.SubMchId)
	if err != nil {
		return nil, err
	}
	return payConf, nil
}

func (s *separateAccountsService) BindUser(c *gin.Context, req *model.ReqSubAccount) error {
	info, err := SeparateAccounts.Info(c, "user_commission,platform_commission,commission,acid,name",
		req.SubMchId)
	set := new(accountSettingService)
	res, err := set.InfoById(info.Acid, "miniprogram")
	if err != nil {
		return err
	}
	share, err := wechat_v3.NewConfig(res).SubAccount(c, req)
	if err != nil {
		return err
	}
	logrus.Info("用户绑定信息：", share)
	return err
}

func (s *separateAccountsService) Apply(c *gin.Context, req *model.ReqSeparateApply) error {
	user, err := Request.User(c)
	if err != nil {
		return err
	}
	req.UserId = user.Id
	req.Acid = user.Acid
	var t tables.SeparateApply
	get, err := orm.Db.Where("sub_mch_id=? and acid=? and user_id=?", req.SubMchId, req.Acid, user.Id).Get(&t)
	if err != nil {
		return err
	}
	if get {
		update, err := orm.Db.Table("separate_apply").Where("id=?", t.Id).Update(req)
		if err != nil {
			return err
		}
		if update == 0 {
			return errors.New("修改失败")
		}
	} else {
		update, err := orm.Db.Table("separate_apply").Insert(req)
		if err != nil {
			return err
		}
		if update == 0 {
			return errors.New("修改失败")
		}
	}
	return err
}
func (s *separateAccountsService) SeparateAccountsV3(c *gin.Context, req *model.SeparateApply) error {
	// 先分账给用户
	mchId := req.MchId
	subMchId := req.SubMchId
	orderId := req.OutTradeNo
	totalFee := req.TotalFee
	info, err := SeparateAccounts.Info(c, "user_commission,platform_commission,commission,acid,name",
		subMchId)
	// 先分用户
	var data []map[string]interface{}
	// 平台拿出分账金额
	totalFee = totalFee * (gconv.Float64(info.PlatformCommission) / 100)
	// 平台分成
	amount := gconv.Int(gconv.Float64(gconv.Float64(info.UserCommission)*totalFee) / 100)
	data = append(data, map[string]interface{}{
		"order_id":      orderId,
		"type":          "PERSONAL_OPENID",
		"account":       req.Openid,
		"amount":        amount,
		"description":   "购买赠送",
		"platform_name": info.Name,
		"group_name":    "用户返额",
		"sub_mch_id":    subMchId,
		"commission":    info.UserCommission,
	})
	remainingAmount := amount
	// 分发区域代理
	list := new(separateAccountsSubService)
	sub, err := list.ListBySubMchId("account,type", subMchId)
	if err != nil {
		return err
	}
	for _, item := range sub {
		amount = gconv.Int(gconv.Float64(gconv.Float64(item.Group.Commission)*totalFee) / 100)
		data = append(data, map[string]interface{}{
			"order_id":      orderId,
			"type":          item.Type,
			"account":       item.Account,
			"amount":        amount,
			"description":   "商户抽成",
			"platform_name": info.Name,
			"group_name":    item.Group.Name,
			"sub_mch_id":    subMchId,
			"commission":    item.Group.Commission,
		})
		remainingAmount += amount
	}
	// 分发平台
	amount = gconv.Int(gconv.Float64(gconv.Float64(info.Commission)*totalFee) / 100)
	data = append(data, map[string]interface{}{
		"order_id":      orderId,
		"type":          "MERCHANT_ID",
		"account":       mchId,
		"amount":        amount,
		"description":   "平台抽成",
		"platform_name": info.Name,
		"group_name":    "平台",
		"sub_mch_id":    subMchId,
		"commission":    info.Commission,
	})
	remainingAmount += amount
	remainingAmount = gconv.Int(totalFee) - remainingAmount
	if remainingAmount > 0 {
		data = append(data, map[string]interface{}{
			"order_id":      orderId,
			"type":          "MERCHANT_ID",
			"account":       mchId,
			"amount":        remainingAmount,
			"description":   "平台抽成",
			"platform_name": info.Name,
			"group_name":    "剩余金额转入",
			"sub_mch_id":    subMchId,
			"commission":    info.Commission,
		})
	}

	// 插入数据表
	var shareOrder []tables.ShareOrder
	err = mapstructure.Decode(data, &shareOrder)
	if err != nil {
		return err
	}
	s2 := new(shareOrderService)
	err = s2.SaveAll(&shareOrder)
	if err != nil {
		return nil
	}
	// 唯一数组
	newData := s.mergeData(data)
	set := new(accountSettingService)
	res, err := set.InfoById(info.Acid, "miniprogram")
	if err != nil {
		return err
	}
	share, err := wechat_v3.NewConfig(res).Share(map[string]string{
		"sub_mch_id":     subMchId,
		"transaction_id": req.TransactionId,
		"out_order_no":   req.OutTradeNo,
	}, newData)
	if err != nil {
		return err
	}
	if share.ResultCode == "FAIL" {
		return errors.New(share.ErrCodeDes)
	}
	return nil
}
